#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>

#define log_err(fmt, ...) printf("[%s:%d %s]ERR: " fmt "\r\n", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__);

#ifndef semun
union semun
{
    int              val;
    struct semid_ds *buf;
    unsigned short  *array;
    struct seminfo  *__buf;
};
#endif

int ctest_sem_init(key_t key, int init_num)
{
    log_err("semget start key = %d init_num = %d", key, init_num);
    int semid = semget(key, 1, IPC_CREAT | IPC_EXCL);
    if (semid == -1) {
        log_err("semget fail errno = [%d]%s", errno, strerror(errno));
        return -1;
    }

    union semun sem_init = {0};
    sem_init.val = init_num;
    if (semctl(semid, 0, SETVAL, sem_init) == -1) {
        log_err("semctl fail errno = [%d]%s", errno, strerror(errno));
        return -1;
    }

    return semid;
}

int ctest_sem_wait(int semid)
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op  = -1;
    buf.sem_flg = SEM_UNDO;

    if (semop(semid, &buf, 1) == -1) {
        log_err("semop ret = %u", buf.sem_op);
        return -1;
    }

    return 0;
}

int ctest_sem_post(int semid)
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op  = 1;
    buf.sem_flg = SEM_UNDO;

    if (semop(semid, &buf, 1) == -1) {
        log_err("semop ret = %u", buf.sem_op);
        return -1;
    }

    return 0;
}

void ctest_sem_destroy(int semid)
{
    if (semctl(semid, 0, IPC_RMID, NULL) == -1) {
        log_err("semctl fail errno = [%d]%s", errno, strerror(errno));
        return;
    }
}

#define DELAY_SECOND 2

void* newThreadAction(void* args)
{
    pthread_detach(pthread_self());
    int *semid = args;
    log_err("this is new thread!");
    log_err("delay %us", DELAY_SECOND);
    sleep(DELAY_SECOND);
    int ret = ctest_sem_post(*semid);
    log_err("sem_post ret = %u", ret);
    return NULL;
}

int main()
{
    key_t key = ftok("./ctest_systemv_sem.out", 1);
    int semid = ctest_sem_init(key, 0);
    if (semid == -1) {
        log_err("ctest_sem_init ret = %u", semid);
        return 0;
    }

    pthread_t tid;

    int ret = pthread_create(&tid, NULL, newThreadAction, &semid);
    if (ret != 0) {
        log_err("pthread_create ret = %u", ret);
        return 0;
    }
    log_err("sem_wait start");
    ret = ctest_sem_wait(semid);
    log_err("sem_wait ret = %u", ret);

    ctest_sem_destroy(semid);
    return 0;
}